#include "stdafx.h"
#include <vex.tests/msperf/tests.exe/Perf_test_runner_range_iteration.h>

namespace tests_exe { namespace range_iteration {
    namespace vi = vex::iterable;

    //namespace {
        typedef boost::any_range<int, boost::random_access_traversal_tag, int&, std::ptrdiff_t> boost_any_range_t;
        typedef vi::contract::range<vi::random_access, int&> vex_raw_range_t;
        typedef vi::any_range<vi::random_access_range_tag, int&> vex_any_range_t;

#ifdef _DEBUG
        static const int c_iteration_count = 100;
#else
        static const int c_iteration_count = 1000000;
#endif
        template<class TItr>
        static void internal_execute(TItr p_beg, TItr p_end)
        {
            for (; p_beg != p_end; ++p_beg) {
                ++(*p_beg);
            }
        }

        static void internal_execute(boost_any_range_t p_r)
        {
            for (auto v : p_r) {
                ++v;
            }
        }

        static void internal_execute(vex_raw_range_t* p_r)
        {
            while (p_r->move_front()) {
                ++p_r->front();
            }
        }

        static void internal_execute(vex_any_range_t p_r)
        {
            while (p_r.move_front()) {
                ++p_r.front();
            }
        }

        template<class TRange>
        static void internal_execute(TRange&& p_r)
        {
            while (p_r.move_front()) {
                ++p_r.front();
            }
        }
    //}

    class VEX_ABSTRACT Perf_base : public Perf_test_runner_base {
    protected:
        std::vector<int> m_values;
        
        Perf_base() 
            : m_values(c_iteration_count, 0) {}
    };

    class VEX_ABSTRACT Perf_native
        : public Perf_base
    {
    public:
        virtual const char* name() const
        {
            return "range_iteration_native";
        }

        virtual void run()
        {
            auto tp = boost::chrono::high_resolution_clock::now();
            internal_execute(m_values.begin(), m_values.end());
            this->set_duration_ticks((boost::chrono::high_resolution_clock::now() - tp).count());
        }
    };

    class VEX_ABSTRACT Perf_vex_range
        : public Perf_base
    {
    public:
        virtual const char* name() const
        {
            return "range_iteration_vex_range";
        }

        virtual void run()
        {
            auto tp = boost::chrono::high_resolution_clock::now();
            internal_execute(vi::make_range_from_iterator(m_values.begin(), m_values.end()));
            this->set_duration_ticks((boost::chrono::high_resolution_clock::now() - tp).count());
        }
    };

    class VEX_ABSTRACT Perf_vex_range_init_excluded
        : public Perf_base
    {
    public:
        virtual const char* name() const
        {
            return "range_iteration_vex_range_init_excluded";
        }

        virtual void run()
        {
            auto r = vi::make_range_from_iterator(m_values.begin(), m_values.end());
            auto tp = boost::chrono::high_resolution_clock::now();
            internal_execute(r);
            this->set_duration_ticks((boost::chrono::high_resolution_clock::now() - tp).count());
        }
    };

    class VEX_ABSTRACT Perf_boost_any_range
        : public Perf_base
    {
        boost_any_range_t m_range;
    public:
        virtual const char* name() const
        {
            return "range_iteration_boost_any_range";
        }

        virtual void run()
        {
            auto t = boost::chrono::high_resolution_clock::now();
            range_iteration::internal_execute(m_range);
            set_duration_ticks((boost::chrono::high_resolution_clock::now() - t).count());
        }

        Perf_boost_any_range()
            : Perf_base()
        {
            m_range = boost_any_range_t(m_values.begin(), m_values.end());
        }
    };

    class VEX_ABSTRACT Perf_boost_any_range_with_init_and_destroy
        : public Perf_base
    {
    public:
        virtual const char* name() const
        {
            return "range_iteration_boost_any_range_with_init_and_destroy";
        }

        virtual void run()
        {
            auto t = boost::chrono::high_resolution_clock::now();
            range_iteration::internal_execute(boost_any_range_t(m_values.begin(), m_values.end()));
            set_duration_ticks((boost::chrono::high_resolution_clock::now() - t).count());
        }
    };

    class VEX_ABSTRACT Perf_vex_any_range
        : public Perf_base
    {
        vex_any_range_t m_range;
    public:
        virtual const char* name() const
        {
            return "range_iteration_vex_any_range";
        }

        virtual void run()
        {
            auto t = boost::chrono::high_resolution_clock::now();
            range_iteration::internal_execute(m_range);
            set_duration_ticks((boost::chrono::high_resolution_clock::now() - t).count());
        }

        Perf_vex_any_range()
            : Perf_base()
        {
            vex_any_range_t(vi::make_any_range(m_values))
                .swap(m_range);
        }
    };

    class VEX_ABSTRACT Perf_vex_any_range_with_init_and_destroy
        : public Perf_base
    {
    public:
        virtual const char* name() const
        {
            return "range_iteration_vex_any_range_with_init_and_destroy";
        }

        virtual void run()
        {
            auto t = boost::chrono::high_resolution_clock::now();
            range_iteration::internal_execute(vi::make_any_range(m_values));
            set_duration_ticks((boost::chrono::high_resolution_clock::now() - t).count());
        }
    };

    class VEX_ABSTRACT Perf_vex_raw_range
        : public Perf_base
    {
        vex_any_range_t m_range;
    public:
        virtual const char* name() const
        {
            return "range_iteration_vex_raw_range";
        }

        virtual void run()
        {
            auto raw = m_range.get();
            auto tp = boost::chrono::high_resolution_clock::now();
            range_iteration::internal_execute(raw);
            set_duration_ticks((boost::chrono::high_resolution_clock::now() - tp).count());
        }

        Perf_vex_raw_range()
            : Perf_base()
        {
            vex_any_range_t(vi::make_any_range(m_values))
                .swap(m_range);
        }
    };

    class VEX_ABSTRACT Perf_vex_raw_range_with_init_and_destroy
        : public Perf_base
    {
    public:
        virtual const char* name() const
        {
            return "range_iteration_vex_raw_range_with_init_and_destroy";
        }

        virtual void run()
        {
            auto tp = boost::chrono::high_resolution_clock::now();
            if (vex_raw_range_t* raw = vi::make_raw_range(vi::make_range_from_iterator(m_values))) {
                range_iteration::internal_execute(raw);
                raw->release();
            }
            set_duration_ticks((boost::chrono::high_resolution_clock::now() - tp).count());
        }
    };

    void append_your_runners(Iperf_test_runner_collector* p_runners)
    {
        if (p_runners) {
            if (auto p = vex::core::make_intrusive_handle<Perf_native>()) {
                p_runners->put(p.get());
            }
            if (auto p = vex::core::make_intrusive_handle<Perf_vex_range>()) {
                p_runners->put(p.get());
            }
            if (auto p = vex::core::make_intrusive_handle<Perf_vex_range_init_excluded>()) {
                p_runners->put(p.get());
            }
            if (auto p = vex::core::make_intrusive_handle<Perf_boost_any_range>()) {
                p_runners->put(p.get());
            }
            if (auto p = vex::core::make_intrusive_handle<Perf_vex_any_range>()) {
                p_runners->put(p.get());
            }
            if (auto p = vex::core::make_intrusive_handle<Perf_vex_raw_range>()) {
                p_runners->put(p.get());
            }
            if (auto p = vex::core::make_intrusive_handle<Perf_boost_any_range_with_init_and_destroy>()) {
                p_runners->put(p.get());
            }
            if (auto p = vex::core::make_intrusive_handle<Perf_vex_any_range_with_init_and_destroy>()) {
                p_runners->put(p.get());
            }
            if (auto p = vex::core::make_intrusive_handle<Perf_vex_raw_range_with_init_and_destroy>()) {
                p_runners->put(p.get());
            }
        }
    }
}}